
/*
 *    Filename:	ProcessControl.m 
 *    Created :	Sat Dec 21 00:23:34 1991 
 *    Author  :	Vince DeMarco
 *		<vince@whatnxt.cuc.ab.ca>
 */


/* Generated by Interface Builder */

#import "ProcessControl.h"

#import <appkit/nextstd.h>

#import <appkit/Window.h>
#import <appkit/Panel.h>
#import <appkit/NXBrowser.h>
#import <appkit/NXBrowserCell.h>
#import <appkit/Application.h>
#import <appkit/TextField.h>
#import <appkit/defaults.h>

#import <sys/types.h>
#import <sys/stat.h>
#import <sys/dir.h>
#import <sys/file.h>
#import <libc.h>

#import "thread.h"
#import "CircularSlider.h"
#import "Freeze.h"
#import "basename.h"
#import "dirname.h"

#if !defined(S_ISDIR) && defined(S_IFDIR)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

#if !defined(S_ISREG) && defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif

int is_not_dot_or_dotdot (char *name);


@implementation ProcessControl



void updateTimer(DPSTimedEntry teNumber, double now, char *userData)
{
    [(id)userData update:(id)userData];
}

- init
{
    self = [super init];
    updatetime = atof((char *)NXGetDefaultValue("Freeze","UpdateTime"));

    bzero(buffer.buf,BUFFER_SIZE*BUFFER_WIDTH);
    buffer_count = buffer.end = buffer.start = buffer.count = 0;

    mutex_init(&buffer.lock);
    mutex_set_name(&buffer.lock,"Buffer Mutex Lock");

    condition_init(&buffer.not_empty);
    condition_set_name(&buffer.not_empty,"Buffer not Empty");

    condition_init(&buffer.not_full);
    condition_set_name(&buffer.not_full,"Buffer not Full");

    /* Start up background thread */
    consumer_thread = cthread_fork(consumer,(any_t)(&buffer)); /* Done like this so it is possible
								* to change the priority of the
								* background thread easily 
								* This also allows you to terminate
								* the thread, because now the thread
								* descriptor
								*/
    cthread_set_name(consumer_thread, "Consumer Process");     
    cthread_detach(consumer_thread); 

    [NXApp loadNibSection:"Process.nib" owner:self withNames:NO fromZone:[self zone]];
    [window setMiniwindowIcon:"FreezeProcs"];

    updateTimedEntry = DPSAddTimedEntry(updatetime, updateTimer,(void *)self, NX_RUNMODALTHRESHOLD);

    return self;
}

- free
{
    DPSRemoveTimedEntry(updateTimedEntry);
    return [super free];
}

- setupdateTime:(double)value
{
    updatetime = value;
    DPSRemoveTimedEntry(updateTimedEntry);
    updateTimedEntry = DPSAddTimedEntry(updatetime, updateTimer,(void *)self, NX_RUNMODALTHRESHOLD);
    return self;
}

- (double)updateTime
{
    return updatetime;
}

- displayProcesses:sender
{
    [window makeKeyAndOrderFront:sender];
    return self;
}

- addFile:(char *)filename
{
    char *directory;
    struct stat stats;

    if (access(filename,R_OK|F_OK) < 0){
	NXRunAlertPanel("Can't Process File",
			"You don't have read access to file %s",
			"Okay",NULL,NULL,filename);
	return self;
    }
    if (access(directory = dirname(filename),W_OK) < 0){
	NXRunAlertPanel("Can't Process File",
			"Can't process file %s because you do not have write access to directory %s",
			"Okay",NULL,NULL,basename(filename),directory);
	return self;
    }	
    stat(filename,&stats);
    if (!S_ISREG(stats.st_mode)){
	NXRunAlertPanel("Can't Process File",
			"%s is not a regular file left unchanged",
			"Okay",NULL,NULL,filename);
	return self;
    }
    buffer_put(filename,&buffer);
    return self;
}

- addDirectory: (char *)dirname;
{
    int cc, fd;
    char dirbuf[8*MAXPATHLEN];
    struct direct *dp;
    long basep;
    char *buf;
    char tempbuff[MAXPATHLEN];
    struct stat stats;

    stat(dirname,&stats);
    if (S_ISDIR(stats.st_mode) && (access(dirname,(W_OK|R_OK)|X_OK) == 0)){ /* Check if a dir and W,R,X okay */
	if ((fd = open(dirname, O_RDONLY, 0644)) > 0) {
	    cc = getdirentries(fd, (buf = dirbuf), 8*MAXPATHLEN, &basep);
	    while (cc) {
		dp = (struct direct *)buf;
		sprintf(tempbuff,"%s/%s",dirname,dp->d_name);
		stat(tempbuff,&stats);
		if (S_ISDIR(stats.st_mode) && is_not_dot_or_dotdot(tempbuff)){
		    [self addDirectory:tempbuff];
		}
		if (!S_ISDIR(stats.st_mode) && (access(tempbuff,R_OK|F_OK) == 0) && !access(dirname,W_OK) 
     		                                                                 && S_ISREG(stats.st_mode)){
			buffer_put(tempbuff,&buffer);                 
		}
		buf += dp->d_reclen;
		if (buf >= dirbuf + cc) {
		    cc = getdirentries(fd, (buf = dirbuf), 8*MAXPATHLEN, &basep);
		}
	    } /* while (cc) */
	    close(fd);
	}
    } 
    return self;
}

/* Taken from GNU fileutils v3.1 (ls.c) 
 * Return non-zero if `name' doesn't end in `.' or `..'
 * This is so we don't try to recurse on `././././. ...' 
 */

int is_not_dot_or_dotdot (char *name)
{
  char *t;

  t = rindex (name, '/');
  if (t)
    name = t + 1;

  if (name[0] == '.'
      && (name[1] == '\0'
          || (name[1] == '.' && name[2] == '\0')))
    return 0;

  return 1;
}


- (BOOL)backgroundjobs
{
    if ((buffer.count > 0) || (current_file[0] != '\000')){
	return YES;
    }else{
	return NO;
    }
}

- killBackground:sender
{

    char *suffix = rindex(current_file,'.');

    if (strcmp(suffix,".F")){        // i can rewite this global because the
	strcat(current_file,".F");   // The program will be exited and the current_file
    }else{                           // variable will never be looked at again.
	*suffix='\000';
    }

    if (unlink(current_file) < 0){
	perror("unlink");
    }
    return self;
}

- update:sender
{
    static int count;

    if ( buffer_count != buffer.count ){
	buffer_count = buffer.count;
	[window disableDisplay];
	[browser loadColumnZero];
	[browser displayAllColumns];
	[window reenableDisplay];
	[window display];
    }

    if (current_file[0] == '\000'){
	if (count == 0){
	    [textfield setTextGray:0.3333333];
	    [textfield setStringValue:"No Background Processes"];
	    [procfield setStringValue:NULL];
	    [circSlider setIntValue:360];
	    count = 1;
	}
    }else{
	[textfield setTextGray:0];
	[textfield setStringValue:basename(current_file)];
	[procfield setStringValue:current_process];
	[circSlider setIntValue:( (percent_done < 10) ? 0 : percent_done - 10)];
	count = 0;
    }
    return self;
}

/* BROWSER DELEGATE METHODS */
- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
// Load a specific Value in cell at row,column
{
    
    int buffer_index = (row+buffer.end) % BUFFER_SIZE;

    [cell setStringValueNoCopy:buffer.buf[buffer_index]];
    [cell setLeaf:YES];
    return self;
}

- (int)browser:sender getNumRowsInColumn:(int)column
// return Number of rows in column 
{
    return buffer.count;
}


/* WINDOW DELEGATE METHODS */
- windowWillResize:sender toSize:(NXSize *)frameSize
{
    frameSize->width  = MAX(frameSize->width,343.0);
    frameSize->width  = MIN(frameSize->width,500.0);
    frameSize->height = MAX(frameSize->height,258.0);
    return self;
}

@end
